﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using WLFramework.Json;
using WLFramework.Web;

namespace GenericHtml
{
    public class AjaxProxy : ILoginInfo
    {
        private static Dictionary<string, TableCommand> _caches =
            new Dictionary<string, TableCommand>();

        public string Action { get; private set; }

        public UserInfo LoginUser { get; set; }

        public DynamicTable Table { get; set; }

        private HttpContext context;

        public AjaxProxy(string table, string action)
        {
            this.Table = MetadataManager.GetTable(table);
            if (this.Table == null)
                throw new ArgumentNullException("table");

            this.Action = action;
        }

        public void Execute(HttpContext context)
        {
            this.LoginUser = context.Session["current_user"] as UserInfo;
            if (this.LoginUser == null)
            {
                // 登录失效 处理
                context.Response.Write(JSON.Instance.ToJSON(GenericConfigure.LoginFailed));
                return;
            }

            this.context = context;

            switch (Action)
            {
                case "add":
                    this.ExecuteAdd();
                    break;

                case "update":
                    this.ExecuteUpdate();
                    break;

                case "delete":
                    this.ExecuteDelete();
                    break;

                case "query":
                    this.ExecuteQuery();
                    break;

                case "get":
                    break;

                default:
                    return;
            }

            GenericConfigure.Log(this.LoginUser.U_ID, this.Table.Id, Action);
        }

        private void ExecuteAdd()
        {
            try
            {
                var query = this.Table.Columns.Where(k => !k.IsSystemDefault);
                var list = GetAllKeyValuePair(query.ToArray());

                this.InsertObject(list);

                context.Response.Write(JSON.Instance.ToJSON(JSONResult.Success));
            }
            catch (Exception /*ex*/)
            {
                context.Response.Write(JSON.Instance.ToJSON(JSONResult.Failed));
            }
        }

        private void ExecuteUpdate()
        {
            try
            {
                var query = this.Table.Columns.Where(k => !k.IsSystemDefault && k.Edit);
                var list = GetAllKeyValuePair(query.ToArray());
                var id = this.context.GetValueByName<int>("Id");

                this.UpdateObject(id, list);

                context.Response.Write(JSON.Instance.ToJSON(JSONResult.Success));
            }
            catch (Exception /*ex*/)
            {
                context.Response.Write(JSON.Instance.ToJSON(JSONResult.Failed));
            }
        }

        private void ExecuteDelete()
        {
            try
            {
                var id = this.context.GetValueByName<int>("Id");

                this.DeleteObject(id);

                context.Response.Write(JSON.Instance.ToJSON(JSONResult.Success));
            }
            catch (Exception /*ex*/)
            {
                context.Response.Write(JSON.Instance.ToJSON(JSONResult.Failed));
            }
        }

        private void ExecuteGet()
        {
            try
            {
                var id = this.context.GetValueByName<int>("Id");
                var row = GetObject(id);
                context.Response.Write(JSON.Instance.ToJSON(row));
            }
            catch (Exception /*ex*/)
            {
                context.Response.Write(JSON.Instance.ToJSON(JSONResult.Failed));
            }
        }

        private void ExecuteQuery()
        {
            try
            {
                int? page = context.GetValueByName<int?>("page");
                int? rows = context.GetValueByName<int?>("rows");
                var dt = SelectObjects(page, rows);

                var result = new
                {
                    total = Count(),
                    rows = dt.Tables[0].Rows
                };

                context.Response.Write(JSON.Instance.ToJSON(result));
            }
            catch (Exception /*ex*/)
            {
                context.Response.Write(JSON.Instance.ToJSON(JSONResult.Failed));
            }
        }

        private void InsertObject(IList<KeyValuePair<string, object>> values)
        {
            using (var db = GenericConfigure.CreateDbManager())
            {
                db.BeginTransaction();

                var parms = new List<IDbDataParameter>();
                parms.Add(db.Parameter("@CreateUser", this.LoginUser.U_ID));
                parms.Add(db.Parameter("@UpdateUser", this.LoginUser.U_ID));
                for (int i = 0; i < values.Count; i++)
                {
                    parms.Add(db.Parameter("@" + values[i].Key, values[i].Value));
                }

                db.SetCommand(this.Table.Command.InsertCommand, parms.ToArray())
                  .ExecuteNonQuery();

                db.CommitTransaction();
            }
        }

        private void UpdateObject(int id, IList<KeyValuePair<string, object>> values)
        {
            using (var db = GenericConfigure.CreateDbManager())
            {
                db.BeginTransaction();

                var parms = new List<IDbDataParameter>();
                parms.Add(db.Parameter("@Id", id));
                parms.Add(db.Parameter("@UpdateUser", this.LoginUser.U_ID));
                for (int i = 0; i < values.Count; i++)
                {
                    parms.Add(db.Parameter("@" + values[i].Key, values[i].Value));
                }

                db.SetCommand(this.Table.Command.UpdateCommand, parms.ToArray())
                  .ExecuteNonQuery();

                db.CommitTransaction();
            }
        }

        private void DeleteObject(int id)
        {
            using (var db = GenericConfigure.CreateDbManager())
            {
                db.BeginTransaction();
                db.SetCommand(this.Table.Command.DeleteCommand, db.Parameter("@Id", id))
                  .ExecuteNonQuery();
                db.CommitTransaction();
            }
        }

        private DataRow GetObject(int id)
        {
            using (var db = GenericConfigure.CreateDbManager())
            {
                var dt = db.SetCommand(
                                string.Format("SELECT * FROM [{0}]", this.Table.Name),
                                db.Parameter("@Id", id))
                           .ExecuteDataTable();

                return dt.Rows.Count > 0 ? dt.Rows[0] : null;
            }
        }

        private DataSet SelectObjects(int? page, int? rows)
        {
            using (var db = GenericConfigure.CreateDbManager())
            {
                string query = this.Table.Command.SelectCommand;

                if (page != null && rows != null)
                {
                    query = string.Format(
                        "SELECT * FROM ({0}) __TEMP WHERE __RowId BETWEEN {1} AND {2}",
                        query, (page - 1) * rows + 1, page * rows);
                }

                return db.SetCommand(query)
                         .ExecuteDataSet();
            }
        }

        private int Count()
        {
            using (var db = GenericConfigure.CreateDbManager())
            {
                string query = string.Format("SELECT COUNT(Id) FROM {0}", this.Table.Name);
                return db.SetCommand(query)
                         .ExecuteScalar<int>();
            }
        }

        private List<KeyValuePair<string, object>> GetAllKeyValuePair(DynamicColumn[] columns)
        {
            List<KeyValuePair<string, object>> list =
                new List<KeyValuePair<string, object>>();
            foreach (var col in columns)
            {
                bool required = col.Required;
                switch (col.TypeId)
                {
                    case ColumnType.STRING:
                        var tmp = GetKeyValuePair<string>(col.Name);
                        if (required && tmp.Value == null)
                            throw new ArgumentNullException(col.Name);
                        list.Add(tmp);
                        break;

                    case ColumnType.ID:
                    case ColumnType.DICTIONARY:
                        if (required)
                        {
                            list.Add(GetKeyValuePair<int>(col.Name));
                        }
                        else
                        {
                            list.Add(GetKeyValuePair<int?>(col.Name));
                        }

                        break;

                    case ColumnType.DECIMAL:
                        if (required)
                        {
                            list.Add(GetKeyValuePair<decimal>(col.Name));
                        }
                        else
                        {
                            list.Add(GetKeyValuePair<decimal?>(col.Name));
                        }
                        break;

                    case ColumnType.DATE:
                    case ColumnType.DATETIME:
                    case ColumnType.TIME:
                        if (required)
                        {
                            list.Add(GetKeyValuePair<DateTime>(col.Name));
                        }
                        else
                        {
                            list.Add(GetKeyValuePair<DateTime?>(col.Name));
                        }
                        break;
                }
            }

            return list;
        }

        private KeyValuePair<string, object> GetKeyValuePair<T>(string name)
        {
            return new KeyValuePair<string, object>(
                name,
                context.GetValueByName<T>(name));
        }
    }
}
